package net.bxx2004.pandalibloader;

import net.bxx2004.pandalib.PandaLib;
import net.bxx2004.pandalib.bukkit.commands.BukkitCommand;
import net.bxx2004.pandalib.bukkit.commands.BukkitSubCommand;
import net.bxx2004.pandalib.bukkit.commands.PCommand;
import net.bxx2004.pandalib.bukkit.commands.SubCommand;
import net.bxx2004.pandalib.bukkit.file.data.config.Config;
import net.bxx2004.pandalib.bukkit.file.data.config.Node;
import net.bxx2004.pandalib.bukkit.file.PYml;
import net.bxx2004.pandalib.bukkit.listener.BukkitListener;
import net.bxx2004.pandalib.bukkit.manager.Lang;
import net.bxx2004.pandalib.bukkit.task.register.AutoConstruct;
import net.bxx2004.pandalib.bukkit.util.PDownLoad;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.event.Listener;
import org.reflections.Reflections;
import org.reflections.scanners.MethodAnnotationsScanner;
import org.reflections.util.ClasspathHelper;
import org.reflections.util.ConfigurationBuilder;

import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import static org.bukkit.Bukkit.getServer;

public class LoadActiveItem {
    private String packageName;
    private PandaLibPlugin<BukkitPlugin> plugin = PandaLib.initPandaLibPlugin;
    private Reflections reflections;
    public void init(String packageName){
        this.packageName = packageName;
        this.reflections = new Reflections(packageName);
    }
    public void init(String packageName,Reflections ref){
        this.packageName = packageName;
        this.reflections = ref;
    }
    public final void setGame(){
        Set<Class<?>> cc = reflections.getTypesAnnotatedWith(Config.class);
        cc.forEach(c ->{
            PYml yml = new PYml(c.getAnnotation(Config.class).path(),true);
            for (Field cnode : c.getDeclaredFields()) {
                Node node = cnode.getAnnotation(Node.class);
                if (node == null){
                    continue;
                }
                cnode.setAccessible(true);
                try {
                    if (node.key().equals("useVariableName")){
                        if (yml.get(cnode.getName()) != null){
                            cnode.set(null,yml.get(cnode.getName()));
                        }else {
                            yml.set(cnode.getName(),cnode.get(null));
                            cnode.set(null,yml.get(cnode.getName()));
                        }
                    }else {
                        if (yml.get(node.key()) != null){
                            cnode.set(null,yml.get(node.key()));
                        }else {
                            yml.set(node.key(),cnode.get(null));
                            cnode.set(null,yml.get(node.key()));
                        }
                    }
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        });
        Set<Class<?>> ac = reflections.getTypesAnnotatedWith(AutoConstruct.class);
        try {
            for (Class a : ac){
                a.getClass().getDeclaredConstructor().newInstance();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        Set<Class<?>> li = reflections.getTypesAnnotatedWith(BukkitListener.class);
        List<Listener> listeners = new ArrayList<>();
        for (Class a : li){
            Listener listener = null;
            try {
                listener = (Listener) a.getDeclaredConstructor().newInstance();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            }
            listeners.add(listener);
        }
        for (Listener l : listeners){
            getServer().getPluginManager().registerEvents(l,plugin.getPlugin());
        }

        String pn = plugin.getPlugin().getName();
        Set<Class<?>> mainCommands = reflections.getTypesAnnotatedWith(BukkitCommand.class);
        for (Class c : mainCommands){
            if (Bukkit.getPluginManager().isPluginEnabled(pn)){
                for (Annotation annoo : c.getAnnotationsByType(BukkitCommand.class)){
                    BukkitCommand anno = (BukkitCommand) annoo;
                    PCommand command = null;
                    try {
                        command = (PCommand) c.newInstance();
                    } catch (InstantiationException e) {
                        e.printStackTrace();
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                    PCommand.registerCommand(plugin.getPlugin(),anno.name(),command);
                    PCommand.commandMap.put(anno.name(),new ArrayList<>());
                }
            }else {
            }
        }
        Set<Class<?>> subCommandsT = reflections.getTypesAnnotatedWith(BukkitSubCommand.class);
        Reflections reflections1 = new Reflections( new ConfigurationBuilder()
                .setUrls(ClasspathHelper.forPackage(packageName))
                .addScanners(new MethodAnnotationsScanner()));
        Set<Method> subCommandsM = reflections1.getMethodsAnnotatedWith(BukkitSubCommand.class);
        for (Class s : subCommandsT){
            try {
                Object o = s.newInstance();
                for (Annotation anno : s.getDeclaredAnnotations()){
                    BukkitSubCommand sub = (BukkitSubCommand) anno;
                    SubCommand subcommands = new SubCommand() {
                        @Override
                        public boolean performCommand(CommandSender sender, String[] strings) {
                            if (sub.permission() != null){
                                if (sender.hasPermission(sub.permission())){
                                    if (strings[0].equalsIgnoreCase(sub.usage().split(" ")[0])){
                                        try {
                                            s.getDeclaredMethod("performCommand",CommandSender.class,String[].class).invoke(o,sender,strings);
                                        } catch (Exception e) {
                                            e.printStackTrace();
                                        }
                                    }
                                }
                            }else {
                                if (strings[0].equalsIgnoreCase(sub.usage().split(" ")[0])){
                                    try {
                                        s.getDeclaredMethod("performCommand",CommandSender.class,String[].class).invoke(o,sender,strings);
                                    } catch (Exception e) {
                                        e.printStackTrace();
                                    }
                                }
                            }
                            return false;
                        }
                    };
                    subcommands.usage = sub.usage();
                    subcommands.description = sub.description();
                    PCommand.commandMap.get(sub.mainCommand()).add(subcommands);
                }
            }catch (Exception e){
                e.printStackTrace();
            }
        }
        for (Method m : subCommandsM){
            for (Annotation anno : m.getDeclaredAnnotations()){
                if (anno instanceof BukkitSubCommand){
                    String md = Modifier.toString(m.getModifiers());
                    if (!md.contains("static")){
                        continue;
                    }
                    BukkitSubCommand sub = (BukkitSubCommand) anno;
                    SubCommand subcommands = new SubCommand() {
                        @Override
                        public boolean performCommand(CommandSender sender, String[] strings) {
                            if (sub.permission() != null){
                                if (sender.hasPermission(sub.permission())){
                                    if (strings[0].equalsIgnoreCase(sub.usage().split(" ")[0])){
                                        try {
                                            m.invoke(null,sender,strings);
                                        } catch (Exception e) {
                                            e.printStackTrace();
                                        }
                                    }
                                }
                            }else {
                                if (strings[0].equalsIgnoreCase(sub.usage().split(" ")[0])){
                                    try {
                                        m.invoke(null,sender,strings);
                                    } catch (Exception e) {
                                        e.printStackTrace();
                                    }
                                }
                            }
                            return false;
                        }
                    };
                    subcommands.usage = sub.usage();
                    subcommands.description = sub.description();
                    PCommand.commandMap.get(sub.mainCommand()).add(subcommands);
                }
            }
        }
    }
    public final void loadJar(File file){
        try {
            URLClassLoader urlloader = (URLClassLoader) Thread.currentThread().getContextClassLoader();
            Method m = urlloader.getClass().getDeclaredMethod("addURL", URL.class);
            m.setAccessible(true);
            m.invoke(urlloader,new URL("file:" + file.getAbsolutePath()));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public final void setLibs(){
        try {
            for (Class cl : reflections.getTypesAnnotatedWith(Library.class)){
                Library annotation = (Library) cl.getAnnotationsByType(Library.class)[0];
                Class<? extends Libraries> libraryclass = annotation.libs();
                Libraries r = libraryclass.getDeclaredConstructor().newInstance();
                File file = new File(plugin.getPath().split("plugins")[0] + "server/libs");
                if (!file.exists()){
                    file.mkdirs();
                }
                for (String ls : r.url.keySet()){
                    File jarLib = new File(file.getAbsolutePath() + "/" +ls);
                    if (jarLib.exists()){
                        Lang.print(plugin.getPlugin(),"正在加载资源: &f" + jarLib.getName());
                        loadJar(jarLib);
                    }else {
                        String cloudurl = "http://101.43.156.208/lib/" + r.url.get(ls);
                        PDownLoad d = new PDownLoad(cloudurl,jarLib.getAbsolutePath());
                        d.start(false);
                        jarLib = new File(file.getAbsolutePath() + "/" +ls);
                        Lang.print(plugin.getPlugin(),"正在加载资源: &f" + jarLib.getName());
                        loadJar(jarLib);
                    }
                }
                break;
            }
        }catch (Exception e){e.printStackTrace();}
    }
}
